#ifndef __SPLITBUS_H__
#define __SPLITBUS_H__

#include "base/Entity.h"
#include "mem/Bus.h"
#include "util/HashTable.h"
#include "base/QuiesceRequest.h"
#include "base/QuiesceResponse.h"

#define PRIORITY_TIMEOUT 1

class Processor;
class Counter;

class AddressLockInfo {
  public:
    // overloaded to force use of CALLOC/FREE
    NEW_DELETE_OPERATORS_H(AddressLockInfo,NoSuper);
    
    Entity* dev;
    int numRecursion;
    bool recursionDisabled;
    List<Entity*>* stallQueue;
    Entity* nextDev; 
    Time prevScheduleTime;
    
    AddressLockInfo():
            dev(NULL),
            numRecursion(0),
            recursionDisabled(false),
            nextDev(NULL),
            prevScheduleTime(0)
    {
        stallQueue = NEW List<Entity*>;
    }

    ~AddressLockInfo() {
        delete stallQueue;
    }    

    bool addToStallQueue (Entity* _dev, bool front = false) {
        List<Entity*>::iterator iter;

        for (iter = stallQueue->begin(); iter != stallQueue->end(); iter++) {
            if (_dev == (*iter)) {
                return false;
            }
        }  

        if (!front) {
            stallQueue->add(_dev);
        } else {
            stallQueue->push_front(_dev);
        }
        return true;
    }

    bool hasSchedule() {
        return nextDev != NULL;
    }
} ;

class SplitBus : public Entity {
  public:
    // overloaded to force use of CALLOC/FREE
    NEW_DELETE_OPERATORS_H(SplitBus,Entity);
    SplitBus (const char* name,
              bool addrLockOnReq,
              Logger* _busLog,
              Counter* statArbedStores,
              Counter* statArbedLoads,
              Counter* statArbedOther);

    virtual ~SplitBus ();

    void buildBuses(uint width, uint arbDelay, uint bufferSize);

    // void tick()
    //
    // Tell the bus to take a cycle.
    //
    virtual void tick ();

    void registerRequestSnoopDev (Entity* e);
    void registerResponseSnoopDev (Entity* e);
    virtual void snoop (Entity* interconnect, Packet* pk);    
    virtual Result delegateRoute (Entity* _bus, Packet* r);
    void countVotes ();

    virtual bool isSnooping (Entity* interconnect);    

    Bus* getRequestBus ();
    Bus* getResponseBus ();
    virtual uint getNumSnoopDevs (Entity* interconnect, Packet* pk);
  
    virtual void hookRecordTraffic (Bus* b, Packet* packet);
    void printStatistics (FILE* f);
    static bool visitClear (VA addr, AddressLockInfo*& info, void* data);
    static bool visitCheckSchedule (VA addr,
                                    AddressLockInfo*& info,
                                    void* data); 
    Logger* getLogger ();
    virtual Entity* getRoutingEntity ();  
    void incOutstandingStores ();
    void decOutstandingStores ();
    static void incQuiesceReqCount ();
    static void resetQuiesceReqCount ();

  protected:
    StatisticsCollection* statsReqInput; 
    StatisticsCollection* statsReqOutput; 
    StatisticsCollection* statsReqQueue; 
    StatisticsCollection* statsResInput; 
    StatisticsCollection* statsResOutput; 
    StatisticsCollection* statsResQueue; 
    Counter* statArbedStores;
    Counter* statArbedLoads;
    Counter* statArbedOther;

    Logger* busLog;

    // static physical attributes
    //
    Bus*  requestBus;
    Bus*  responseBus; 

    // in this option, any request sharing the same address with
    // a outstanding request is rejected at handle_packet()
    bool  addrLockOnReq;

    // the list of the devices to snoop.
    // we don't assume that they are caches.
    List<Entity*>* reqSnoopDevs;
    List<Entity*>* resSnoopDevs;
    
    HashTable<VA, AddressLockInfo*> lockedAddrInfo;
    int maxRecursion;
    
    virtual Bus* buildBus (char* name,
                           int width,
                           int arbDelay,
                           int bufferSize,
                           StatisticsCollection* statsInput, 
                           StatisticsCollection* statsOutput, 
                           StatisticsCollection* statsQueue);

    virtual Result handle_packet (Packet* r);
    Result handle_snoopACK (Packet* r);
    Result handle_RawFlushRequest (Packet* r);
    virtual bool isHighPriority (Packet* p);
    void addToStallQueue (VA address, Entity* _dev, Packet* r);    
    bool isAddressLocked (VA addr, Entity* _dev, Packet* r);
    void lockAddress (VA addr, Entity* _dev, Packet* r);
    bool releaseLock (VA addr, Entity* _dev, const char* packetName);
    void disableRecursion (VA addr);
    Result tryLock (VA addr, Entity* dev, Packet* r);
    Result handle_QuiesceRequest (Packet* r);
    Result handleDuplicateQuiesceRequest (Packet* r);
    Result handle_QuiesceResponse (Packet* r);
    virtual bool isReadyForQuiesce ();
    void sendQuiesceRequest (QuiesceRequest* qr);

    static uint numQuiesceReqArrived;
    bool            quiesce;
    QuiesceRequest* quiesceRequest;  
    int  outstandingStores;    
};

void defaultSnoopCB (Packet* pk);

#endif // __SPLITBUS_H__
